﻿using Algorithm.Check;
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Algorithm.Check.ExtensionForCRC16;

namespace NewDevice.RS485
{
    /// <summary>
    /// 电容液位计控制类
    /// 提供基于Modbus协议的电容液位计通信和测量功能
    /// </summary>
    public class LiquidLevelGauge
    {
        /// <summary>
        /// 设备名称
        /// </summary>
        private string deviceName = "电容液位计";

        /// <summary>
        /// 串口通信对象
        /// </summary>
        private readonly SerialBase serial;

        /// <summary>
        /// 当前接收的寄存器数据长度
        /// </summary>
        private int currentRecvRegLength;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="bus">串口通信总线对象</param>
        public LiquidLevelGauge(SerialBase bus)
        {
            this.serial = bus;
        }

        /// <summary>
        /// 读取Modbus寄存器数据
        /// </summary>
        /// <param name="slaveId">从机地址(默认3)</param>
        /// <param name="address">寄存器起始地址</param>
        /// <param name="length">要读取的寄存器数量</param>
        /// <returns>读取到的寄存器数据字节数组</returns>
        /// <exception cref="Exception">当返回数据长度与预期不符时抛出异常</exception>
        /// <remarks>
        /// 使用Modbus RTU协议读取保持寄存器(功能码03H)
        /// 返回数据格式: [从机地址][功能码][字节数][数据1...][CRC16]
        /// </remarks>
        private byte[] ReadRegister(int slaveId, int address, int length)
        {
            // 构造Modbus读取寄存器命令
            byte[] cmd = new byte[] {
                (byte)slaveId,
                0x03, // 功能码:读保持寄存器
                (byte)((address & 0xff00) >> 8), // 寄存器地址高字节
                (byte)(address & 0xff), // 寄存器地址低字节
                (byte)((length & 0xff00) >> 8), // 读取数量高字节
                (byte)(length & 0xff) // 读取数量低字节
            };

            // 添加CRC16校验并发送命令
            cmd = cmd.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            currentRecvRegLength = length * 2;
            byte[] recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);

            // 验证返回数据长度
            if (length * 2 != recv[2])
            {
                throw new Exception(message: $"[{deviceName}] ReadRegister错误，返回数据与长度异常");
            }

            // 返回有效数据部分(去除Modbus协议头)
            return recv.Skip(3).Take((recv[2])).ToArray();
        }

        /// <summary>
        /// 检查接收到的寄存器数据是否有效
        /// </summary>
        /// <param name="recv">接收到的数据</param>
        /// <returns>true表示数据有效，false表示数据无效</returns>
        private bool ReadRegisterCheckRecv(byte[] recv)
        {
            // 检查CRC校验和寄存器数据长度
            if (ReceivedDataCheck.FuncModbusCRC16Check(recv))
            {
                return currentRecvRegLength == recv[2];
            }
            return false;
        }

        /// <summary>
        /// 读取当前温度值
        /// </summary>
        /// <param name="slaveId">从机地址</param>
        /// <returns>温度值(单位:℃)</returns>
        /// <remarks>
        /// 从寄存器地址0x02读取1个寄存器(2字节)的温度值
        /// 实际温度 = 寄存器值 / 10.0
        /// </remarks>
        public double GetTemperature(int slaveId)
        {
            var byteTemp = ReadRegister(slaveId, 0x02, 01);
            Array.Reverse(byteTemp); // 转换为小端字节序
            var temp = BitConverter.ToInt16(byteTemp, 0) / 10.0;
            Mylog.Info(message: $" 温度=[{temp}] ", deviceName: deviceName);
            return temp;
        }

        /// <summary>
        /// 读取当前液位值
        /// </summary>
        /// <param name="slaveId">从机地址</param>
        /// <returns>液位值(单位: 根据仪表设定)</returns>
        /// <remarks>
        /// 从寄存器地址0x03读取1个寄存器(2字节)的液位值
        /// 实际液位 = 寄存器值 / 10.0
        /// </remarks>
        public double GetLiquidLevel(int slaveId)
        {
            var byteLevel = ReadRegister(slaveId, 0x03, 01);
            Array.Reverse(byteLevel); // 转换为小端字节序
            var level = BitConverter.ToInt16(byteLevel, 0) / 10.0;
            Mylog.Info(message: $" 液位=[{level}]", deviceName: deviceName);
            return level;
        }

        /// <summary>
        /// 检查设备是否在线
        /// </summary>
        /// <param name="slaveId">从机地址，默认为1</param>
        /// <returns>true表示设备在线，false表示设备离线</returns>
        /// <remarks>
        /// 通过尝试读取温度值来检测设备是否在线
        /// </remarks>
        public bool IsDeviceOnline(int slaveId = 1)
        {
            try
            {
                var val = GetTemperature(slaveId);
                return true;
            }
            catch (Exception ex)
            {
                Mylog.Error(message: $"{deviceName}不在线,{ex.Message}", ex: ex);
                return false;
            }
        }
    }
}